package com.YCH.user.service.Impl;

import com.YCH.model.common.dtos.ResponseResult;
import com.YCH.model.common.enums.AppHttpCodeEnum;
import com.YCH.model.common.enums.AppTaskType;
import com.YCH.model.mappers.app.*;
import com.YCH.model.user.dtos.LvGradeJudgeDto;
import com.YCH.model.user.pojos.*;
import com.YCH.model.user.vo.ApUserLVAndExperienceVo;
import com.YCH.model.user.vo.ApUserLVTaskVO;
import com.YCH.user.service.AppUserLvService;
import com.YCH.utils.common.DateUtils;
import com.YCH.utils.threadlocal.AppThreadLocalUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
@SuppressWarnings("all")
public class AppUserLvServiceImpl implements AppUserLvService {
    @Autowired
    private TbUserMapper tbUserMapper;
    @Autowired
    private TbUserLVMapper tbUserLVMapper;
    @Autowired
    private TbUserExperienceMapper tbUserExperienceMapper;
    @Autowired
    private TbUserLvTaskMapper tbUserLvTaskMapper;
    @Autowired
    private TbUserTaskMapper tbUserTaskMapper;

    private static final Short TASKCOUNT = 0;

    /**
     * 获取当前登录用户等级和经验
     *
     * @return
     */
    @Override
    public ResponseResult ObtainUserLVAndExperience(Long userId) {
        // 1.获取用户信息
        final TbUser User = (TbUser) GetUserInfo(userId).getData();
        // 2.根据用户信息查询用户等级
        TbUserLv tbUserLv = tbUserLVMapper.selectLVById((int) User.getLv().getId());
        if (tbUserLv == null) {
            ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 3.根据用户信息查询用户经验
        TbUserExperience tbUserExperience = tbUserExperienceMapper.selectByUserId(User.getId());
        if (tbUserExperience == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 4.查询当天获得的经验额
        final Integer sumToadyExperience = tbUserTaskMapper.sumTodayExperience(userId, DateUtils.getCurrentDate());
        // 4.封装vo
        ApUserLVAndExperienceVo apUserLVAndExperienceVo = new ApUserLVAndExperienceVo();
        if(sumToadyExperience !=null ){
            apUserLVAndExperienceVo.setTodayExperience(sumToadyExperience);
        }else {
            apUserLVAndExperienceVo.setTodayExperience(0);
        }
        apUserLVAndExperienceVo.setUserId(tbUserExperience.getUserId());
        apUserLVAndExperienceVo.setExperience(tbUserExperience.getExperience());
        apUserLVAndExperienceVo.setEndExperience(tbUserLv.getMaxExperience());
        apUserLVAndExperienceVo.setLV(tbUserLv.getLV());
        return ResponseResult.okResult(apUserLVAndExperienceVo);
    }

    /**
     * 获取当前登录用户的任务详情
     *
     * @return
     */
    @Override
    public ResponseResult ObtainUserTaskInfo(Long userId) {
        // 1.获取用户信息
        TbUser User = (TbUser) GetUserInfo(userId).getData();
        // 2.根据用户等级查询用户可执行的任务
        // 2.1获取用户id
        short lv = User.getLv().getLV();
        // 2.2根据用户id查询用户可执行的任务
        List<TbUserLvTask> UserLvTasks = tbUserLvTaskMapper.selectAllByOpenLV(lv, TbUserLvTaskMapper.Type.LESS.getCode());
        if (UserLvTasks.isEmpty()) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 2.3获取今日可执行任务的次数
        List<Integer> taskIdList = new ArrayList<>();
        UserLvTasks.forEach((tbUserLvTask) -> {
            taskIdList.add(tbUserLvTask.getId());
        });
        // 拿到今日已完成的任务
        List<TbUserTask> tbUserTasks = tbUserTaskMapper.selectCountByUserId(User.getId(), DateUtils.getCurrentDate(), taskIdList);

        // 2.4将用户当前可执行任务封装到vo
        List<ApUserLVTaskVO> ApUserLVTaskVOs = new ArrayList<>();
        if (tbUserTasks.isEmpty()) {
            // 如果查不到今日可执行次数，说明今日没有执行任务
            UserLvTasks.forEach(tbUserLvTask -> {
                        ApUserLVTaskVO apUserLVTaskVO = new ApUserLVTaskVO();
                        apUserLVTaskVO.setId(tbUserLvTask.getId());
                        apUserLVTaskVO.setTaskName(tbUserLvTask.getTaskName());
                        apUserLVTaskVO.setExperience(tbUserLvTask.getExperience());
                        apUserLVTaskVO.setCount(TASKCOUNT);
                        apUserLVTaskVO.setMaxCount(tbUserLvTask.getMaxcount());
                        ApUserLVTaskVOs.add(apUserLVTaskVO);
                    }
            );
        }
        else{
            UserLvTasks.forEach(tbUserLvTask -> {
                tbUserTasks.forEach(tbUserTask -> {
                    ApUserLVTaskVO apUserLVTaskVO = new ApUserLVTaskVO();
                    if (tbUserLvTask.getId() == tbUserTask.getTaskId()) {
                        apUserLVTaskVO.setId(tbUserLvTask.getId());
                        apUserLVTaskVO.setTaskName(tbUserLvTask.getTaskName());
                        apUserLVTaskVO.setExperience(tbUserLvTask.getExperience());
                        apUserLVTaskVO.setCount(tbUserTask.getTaskCount());
                        apUserLVTaskVO.setMaxCount(tbUserLvTask.getMaxcount());
                        ApUserLVTaskVOs.add(apUserLVTaskVO);
                    }else {
                        apUserLVTaskVO.setId(tbUserLvTask.getId());
                        apUserLVTaskVO.setTaskName(tbUserLvTask.getTaskName());
                        apUserLVTaskVO.setExperience(tbUserLvTask.getExperience());
                        apUserLVTaskVO.setCount((short) 0);
                        apUserLVTaskVO.setMaxCount(tbUserLvTask.getMaxcount());
                        ApUserLVTaskVOs.add(apUserLVTaskVO);
                    }
                });
            });
        }
        // 3.5根据用户id查询用户不可执行的任务
        List<TbUserLvTask> UserLvTasksInAbles = tbUserLvTaskMapper.selectAllByOpenLV(lv, TbUserLvTaskMapper.Type.GREATE.getCode());
        if (UserLvTasksInAbles.isEmpty()) {
            // 如果不可执行任务拿不到，直接返回
            return ResponseResult.okResult(ApUserLVTaskVOs);
        }
        UserLvTasksInAbles.forEach(UserLvTasksInAble ->
        {
            ApUserLVTaskVO apUserLVTaskVO = new ApUserLVTaskVO();
            apUserLVTaskVO.setId(UserLvTasksInAble.getId());
            apUserLVTaskVO.setTaskName(UserLvTasksInAble.getTaskName());
            // 前端判断：如果最大次数是零,次数显示0、经验显示保密
            apUserLVTaskVO.setExperience(UserLvTasksInAble.getExperience());
            apUserLVTaskVO.setCount(TbUserLvTaskMapper.Type.LESS.getCode());
            apUserLVTaskVO.setMaxCount(TbUserLvTaskMapper.Type.LESS.getCode());
            ApUserLVTaskVOs.add(apUserLVTaskVO);
        });
        return ResponseResult.okResult(ApUserLVTaskVOs);
    }

    /**
     * 用户签到
     * @param userId
     * @return
     */
    @Override
    public ResponseResult UserSignIn(Long userId) {
        return UserLVCommon(userId, AppTaskType.SIGN_UP,AppHttpCodeEnum.IS_SIGN_INED);
    }

    /**
     * 发布云记获得经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getPublishArticleExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.PUBLISH_ARTICLE,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 评论经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getCommentExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.COMMENT,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 点赞经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getFabulousExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.FABULOUS,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 被点赞经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getFabulousedExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.FABULOUSED,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 回复经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getReplayExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.REPLAY,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 被回复经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getReplayedExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.REPLAYED,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 收集经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getCollectionExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.COLLETE,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 被收集经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getCollectionedExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.COLLETEED,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 关注经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getFollowExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.LIKE,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }
    /**
     * 被关注经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getFollowedExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.FOLLOWED,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }

    /**
     * 申请加精经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getEXQUISITExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.EXQUISIT,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }
    /**
     * 被加精经验
     * @param userId
     * @return
     */
    @Override
    public ResponseResult getExquisitedExperience(Long userId) {
        return UserLVCommon(userId, AppTaskType.EXQUISITED,AppHttpCodeEnum.NUMBER_EXHAUSTED);
    }


// ******************************************实现方法*********************************************
    /**
     * 加经验之前进行升级判断,升级返回true，否则返回false
     * @param User
     * @param Experience
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class)
    private LvGradeJudgeDto GradeUpJudge(TbUser User, AppTaskType appTaskType){
        // 1 尝试在tb_userExperience.experience中根据userId对添加经验值；
        // 2 获取用户当前经验
        final TbUserExperience tbUserExperience = tbUserExperienceMapper.selectByUserId(User.getId());
        // 3 获取等级经验最大值
        final TbUserLv tbUserLv = tbUserLVMapper.selectLVById((int) User.getLv().getId());
        // 4 更新后的经验 = 当前用户经验 + 任务经验
        // 4.1获取任务经验
        final TbUserLvTask tbUserLvTask = tbUserLvTaskMapper.selectAllById(appTaskType.getCode());
        Integer newExperience = tbUserExperience.getExperience() + tbUserLvTask.getExperience();
        Integer result = 0;
        Integer update = 0;
        // 开启事务
        // 5 如果加上经验值后的变量等于tb_userLV.endExperience，则在tb_user.lv中等级加一,并在tb_userExperience.experience中设置经验为0
        if(newExperience == tbUserLv.getMaxExperience().intValue()){
            // 等级加一
            tbUserLv.setId(User.getLv().getId()+1);
            update = tbUserMapper.updateLvById(tbUserLv, User.getId());
            // 将newExperience重置为零
            newExperience = 0;
            // 设置经验为0
            result = tbUserExperienceMapper.updateExperience(User.getId(), newExperience);

        }
        // 6 如果加上经验值后的变量大于tb_userLV.endExperience，则在tb_user.lv中等级加一，并在tb_userExperience.experience中添加溢出经验
        else if(newExperience > tbUserLv.getMaxExperience().intValue()){
            // 等级加一
            tbUserLv.setId(User.getLv().getId()+1);
            update = tbUserMapper.updateLvById(tbUserLv, User.getId());
            // 设置经验为溢出经验
            newExperience -= tbUserLv.getMaxExperience();
            result = tbUserExperienceMapper.updateExperience(User.getId(), newExperience);
        }
        // 7 如果加上经验值后的变量小于tb_userLV.endExperience，则在在tb_userExperience.experience中添加获得的经验
        else {
            // 设置经验为newExperience
            update = 1;
            result = tbUserExperienceMapper.updateExperience(User.getId(), newExperience);
        }
        // 提交事务
        final LvGradeJudgeDto lvGradeJudgeDto = new LvGradeJudgeDto();
        lvGradeJudgeDto.setResult(update == result && update >= 1);
        lvGradeJudgeDto.setAcquireExperience(tbUserLvTask.getExperience());
        lvGradeJudgeDto.setNowExperience(newExperience);
        lvGradeJudgeDto.setLv((int) tbUserLv.getLV());
        return lvGradeJudgeDto;
    }

    /**
     * 获取用户信息
     * @param userId
     * @return
     */
    private ResponseResult GetUserInfo(Long userId){
        // 1.获取用户信息
        TbUser user = AppThreadLocalUtils.getUser();
        // 2.判断用户是否存在
        if (user == null && userId == null) {
            return ResponseResult.errorResult(200, "用户未登录或缺少参数");
        }
        Long id = null;
        if (user == null) {
            id = userId;
        } else {
            id = user.getId();
        }
        TbUser User = tbUserMapper.selectLvByIdDetailed(id);
        if (User == null) {
            ResponseResult.errorResult(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST);
        }
        return ResponseResult.okResult(User);
    }

    /**
     * 任务次数判断
     * @param User      用户（TbUser）
     * @param TaskId    任务id
     * @param TaskCount 任务次数
     * @param appHttpCodeEnum 次数不够返回的数据
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class)
    private AppHttpCodeEnum TaskCountJudge(TbUser User,Integer TaskId,AppHttpCodeEnum appHttpCodeEnum){
        // 1.根据任务id查询任务可执行次数
        final Short TaskCount = tbUserLvTaskMapper.selectMaxTaskCountById(TaskId);
        // 2.将任务id添加到列表
        List<Integer> integers = new ArrayList<>();
        integers.add(TaskId);
        // 3.根据任务id，userId,时间查询当前登录用户今日执行的任务
        List<TbUserTask> tbUserTasks = tbUserTaskMapper.selectCountByUserId(User.getId(), DateUtils.getCurrentDate(), integers);
        // 4.1判断是否已经签到：已经签到：今日份数据存在且count为1
        if(!tbUserTasks.isEmpty() && tbUserTasks.get(0).getTaskCount()>=TaskCount){
            // 3.2若果已经签到，则返回已签到
            return appHttpCodeEnum;
        }
        // 4.3 如果没有签到，则执行今日签到任务
        // 5.1 在tb_userTask中插入执行任务记录,如果为空就插入，否则就更新taskCount
        Integer result = 0;
        // 5.2 开启事务
        if(tbUserTasks.isEmpty()){
            // 如果是空，说明没有数据，就插入
            TbUserTask tbUserTask = new TbUserTask();
            tbUserTask.setUserId(User.getId());
            tbUserTask.setTaskId(TaskId);
            // 因为数据为空，则次数直接设置成1
            tbUserTask.setTaskCount((short) 1);
            tbUserTask.setTaskDate(DateUtils.stringToDate(DateUtils.getCurrentDateTime()));
            result = tbUserTaskMapper.insert(tbUserTask);
        }else {
            // 更新taskCount
            tbUserTasks.get(0).setTaskCount((short) ((tbUserTasks.get(0).getTaskCount()+1)));
            result = tbUserTaskMapper.update(tbUserTasks.get(0));
        }
        // 6.2 未出错 或者 result==1，则提交事务
        // 6.3 否则回滚事务
        if(result != 1){
            return AppHttpCodeEnum.TRY_AGAIN_LATER;
            // 回滚事务
        }
        return AppHttpCodeEnum.SUCCESS;
    }

    /**
     * 判断是否达到相应等级（达到才能执行任务）
     * @param userId
     * @param appTaskType
     * @return
     */
    private Boolean TaskIsOpen(Long userId,AppTaskType appTaskType){
        // 1.拿到开放等级
        final Short openLV = tbUserLvTaskMapper.selectOpenLVById(appTaskType.getCode());
        // 2.获取用户等级
        final TbUser tbUser = tbUserMapper.selectLvByIdDetailed(userId);
        // 3.判断用户等级与开放等级的大小
        if(openLV > tbUser.getLv().getLV()){
            return false;
        }
        return true;
    }

    private ResponseResult UserLVCommon(Long userId,AppTaskType appTaskType,AppHttpCodeEnum appHttpCodeEnum){
        // 1.获取用户信息
        final TbUser User = (TbUser) GetUserInfo(userId).getData();
        // 2.判断给任务是否对执行该任务的用户开放
        final Boolean taskIsOpen = TaskIsOpen(userId, appTaskType);
        if(!taskIsOpen){
            return ResponseResult.okResult(AppHttpCodeEnum.INSUFFICIENT_LEVEL_AUTHORITY);
        }
        // 3.根据签到id，userId,date查询tbUserTask,
        final AppHttpCodeEnum resultCodeEnum = TaskCountJudge(User, appTaskType.getCode(), appHttpCodeEnum);

        // 4.任务次数判断
        if (resultCodeEnum != AppHttpCodeEnum.SUCCESS){
            return ResponseResult.errorResult(resultCodeEnum);
        }
        // 5.尝试给用户增加经验值
        final LvGradeJudgeDto lvGradeJudgeDto = GradeUpJudge(User, appTaskType);
        if(!lvGradeJudgeDto.getResult()){
            return ResponseResult.errorResult(AppHttpCodeEnum.TRY_AGAIN_LATER);
        };
        // 6.返回数据
        return ResponseResult.okResult(lvGradeJudgeDto);
    }
}